﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Velocity4Net.Ctx;
using Velocity4Net.Runtime.Parse.AST;
using Velocity4Net.Errors;
using System.IO;
using Velocity4Net.Runtime.Logging;

namespace Velocity4Net.Runtime.Directives
{
    /**
     * Break directive used for interrupting scopes.
     *
     * @author <a href="mailto:wyla@removethis.sci.fi">Jarkko Viinamaki</a>
     * @author Nathan Bubna
     * @version $Id$
     */
    public class Break : Directive
    {
        private bool scoped = false;

        /**
         * Return name of this directive.
         * @return The name of this directive.
         */
        public override String Name
        {
            get
            {
                return "break";
            }
            set { }
        }

        /**
         * Return type of this directive.
         * @return The type of this directive.
         */
        public override int Type
        {
            get
            {
                return 2;
            }
        }

        /**
         * Since there is no processing of content,
         * there is never a need for an internal scope.
         */
        public override bool ScopeProvided
        {
            get
            {
                return false;
            }
        }

        /**
         *  simple init - init the tree and get the elementKey from
         *  the AST
         * @param rs
         * @param context
         * @param node
         * @throws TemplateInitException
         */
        public override void Init(RuntimeServices rs, InternalContextAdapter context, INode node)
        {
            base.Init(rs, context, node);

            int kids = node.jjtGetNumChildren();
            if (kids > 1)
            {
                throw new VelocityException("The #stop directive only accepts a single scope object at "
                     + Log.formatFileString(this));
            }
            else
            {
                this.scoped = (kids == 1);
            }
        }

        /**
         * Break directive does not actually do any rendering. 
         * 
         * This directive throws a StopCommand which signals either
         * the nearest Scope or the specified scope to stop rendering
         * its content.
         * 
         * @param context
         * @param writer
         * @param node
         * @return never, always throws a StopCommand
         */
        public override bool Render(InternalContextAdapter context, TextWriter writer, INode node)
        {
            if (!scoped)
            {
                throw new StopCommand();
            }

            Object argument = node.jjtGetChild(0).GetValue(context);
            if (argument is Scope)
            {
                ((Scope)argument).Stop();
            }
            else
            {
                throw new VelocityException(node.jjtGetChild(0).Literal() +
                    " is not a valid " + typeof(Scope).Name + " instance at "
                    + Log.formatFileString(this));
            }
            return false;
        }

    }
}
